package deepr.src.graph;

import deepr.exception.DeeprException;
import deepr.filter.DbFilterBehavior;
import deepr.core.*;
import deepr.filter.FilterParser;
import deepr.filter.FilterRelay;
import deepr.lib.FilterLib;
import deepr.lib.GraphQlLib;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.nio.channels.FileLock;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class GraphFilter extends DbFilterBehavior {

  @Override
  public StringBuilder like(FilterParser parser, String object, String property, String paramKey, Object value) {
    Assert.isTrue(value instanceof String, property + " like value must be string");
    parser.getFilterParams().put(paramKey, value);
    return new StringBuilder().append(object).append(".").append(property).append(" CONTAINS($").append(paramKey).append(")");
  }

  @Override
  public StringBuilder between(FilterParser parser, String object, String key, String paramKey, Object value) throws DeeprException {
    Assert.isTrue(value.getClass().isArray(), object + " between value must be array");
    Object[] v = (Object[]) value;
    Assert.isTrue(v.length == 2, "between value len is 2");
    Object min = v[0];
    Object max = v[1];
    String key3 = parser.getParamKey(paramKey);
    String key4 = parser.getParamKey(paramKey);
    key = object + "." + key;
    key = FilterLib.convertKey(parser, key);
    parser.getFilterParams().put(key3, min);
    parser.getFilterParams().put(key4, max);
    return new StringBuilder().append(key).append(" >=$").append(parser.getParamKey(key3)).append(" AND ").append(key).append(" <=$").append(key4);
  }

  @Override
  public StringBuilder restAction(FilterParser parser, String object, String property, String paramKey, Object value) throws DeeprException {
    parser.getFilterParams().put(paramKey, value);
    String key = object + "." + property;
    key = FilterLib.convertKey(parser, key);
    return new StringBuilder().append(key).append(parser.getAction()).append("$").append(paramKey);
  }

  @Override
  public StringBuilder jsonKey(FilterParser parser, String object, PropertyData properData, String property, String[] keys, Object value) throws Exception {
    String realKey = keys[1];
    log.debug("object:{}, property:{}, realKey:{}, value:{}", object, property, realKey, value);
    String paramKey = property + "_" + realKey;
    parser.getFilterParams().put(paramKey, realKey + ":" + value);
    String key = object + "." + property;
    key = FilterLib.convertKey(parser, key);
    return new StringBuilder().append(key).append(" CONTAINS($").append(paramKey).append(")");
  }

  @Override
  public StringBuilder jsonAt(FilterParser parser, String object, String property, String paramKey, Object value) throws DeeprException {
    log.debug("property:{},paramKey:{},value:{}", property, paramKey, value);
//    StringBuilder key = new StringBuilder().append(object).append(".").append(property);
    String key = object + "." + property;
    key = FilterLib.convertKey(parser, key);
    if (value.getClass().isArray()) {
      Object[] v = (Object[]) value;
      new StringBuilder().append("(");
      for (int i = 0; i < v.length; i++) {
        paramKey = parser.getParamKey(property);
        parser.getFilterParams().put(paramKey, FilterLib.joinJsonKeyValue(property, v[i]));
        if (i > 0)
          FilterLib.appendJoiner(new StringBuilder(), " AND ");
        new StringBuilder().append(key).append(" CONTAINS($").append(paramKey).append(")");
      }
      return new StringBuilder().append(")");
    }
    parser.getFilterParams().put(paramKey, FilterLib.joinJsonKeyValue(property, value));
    return new StringBuilder().append(key).append(" CONTAINS($").append(paramKey).append(")");
  }

  @Override
  public StringBuilder valueArray(FilterParser parser, String object, String property, String paramKey, Object value) throws DeeprException {
    parser.getFilterParams().put(paramKey, value);
    String key = object + "." + property;
    key = FilterLib.convertKey(parser, key);
    return new StringBuilder().append(key).append(" IN $").append(paramKey);
  }

  @Override
  public StringBuilder propertyArray(FilterParser parser, String object, String property, String paramKey, Object value) throws DeeprException {
    parser.getFilterParams().put(paramKey, value);
    String key = object + "." + property;
    key = FilterLib.convertKey(parser, key);
    return new StringBuilder().append("$").append(paramKey).append(" IN ").append(key);
  }

  @Override
  public StringBuilder twoArray(FilterParser parser, String object, String property, String paramKey, Object value) throws DeeprException {
    parser.getFilterParams().put(paramKey, value);
    String key = object + "." + property;
    key = FilterLib.convertKey(parser, key);
    return new StringBuilder().append("ANY(X IN ").append(key).append(" WHERE X IN $").append(paramKey).append(")");
  }

  @Override
  public StringBuilder getJsonArrayFilter(FilterParser parser, FilterRelay filterRelay) {
    StringBuilder sb = new StringBuilder();
    for (Map.Entry<String, StringBuilder> entry : filterRelay.getJsonArrayFilter().entrySet()) {
      if (entry.getValue().length() > 0) {
        FilterLib.appendJoiner(sb, parser.getJoiner());
        sb.append(entry.getValue());
      }
    }
    return sb;
  }

  @Override
  public StringBuilder getForeignFilter(FilterParser parser) throws DeeprException {
    HashMap<Integer, HashMap<String, FilterRelay>> depthForeign = getDepthObjectFilter();
    GraphDisposeContext ctx = (GraphDisposeContext) parser.getCtx();
    int depth = depthForeign.size();
    log.debug("filterMap: {}", depthForeign);
    StringBuilder sb = new StringBuilder();
    for (int i = 1; i <= depth; i++) {
      HashMap<String, FilterRelay> objectFilter = depthForeign.getOrDefault(i, new HashMap<>());
      for (Map.Entry<String, FilterRelay> entry : objectFilter.entrySet()) {
        String object = entry.getKey();
        FilterRelay filterRelay = entry.getValue();
        log.debug("depth: {}, object: {}, parent: {}", i, object, filterRelay.getParent());
        StringBuilder objFilterSb = getObjectFilter(parser, object, filterRelay);
        if (objFilterSb.length() > 0) {
          if (sb.length() > 0)
            sb.append(parser.getJoiner());
          sb.append(objFilterSb);
        }
        if (i == 1)
          continue;
        String parent = filterRelay.getParent();
        SchemaData parentSchema = SchemaManager.getSchemaData(parent);
        PropertyData me = parentSchema.getPropertyData(object);
        StringBuilder m = GraphQlLib.getForeignMatch(ctx, parent, object, me);
        ctx.getMatchSb().append(m);
        if (i == depth) {
          log.debug("---------------match filter: {}", ctx.getMatchSb());
          return sb;
        }
      }
    }
    return sb;
  }

  @Override
  public StringBuilder getObjectFilter(FilterParser parser, String object, FilterRelay filterRelay) {
    StringBuilder proSb = super.getPropertyFilter(parser, filterRelay);
    StringBuilder jsonArraySb = getJsonArrayFilter(parser, filterRelay);
    if (proSb.length() == 0)
      return jsonArraySb;
    if (jsonArraySb.length() == 0)
      return proSb;
    return proSb.append(" AND ").append(jsonArraySb);
  }


}
